//
// Created by suzhen on 16-11-11.
//

#ifndef OKREDIS_OKREDISREPLY_H
#define OKREDIS_OKREDISREPLY_H

#include <vector>
#include <map>
#include <string>
#include "hiredis/hiredis.h"

namespace NoSQL {

    class OkRedisReplyType {

    public:

        explicit OkRedisReplyType(const redisReply *rep);

    public:
        bool hasValue() const;

        bool isReplyOk() const;

        bool isStatusType() const;

        bool isNilType() const;

        bool isIntType() const;

        bool isStringType() const;

        bool isArrayType() const;

    private:
        int replyType_;

    };

    //Util Conv RedisReply to String
    extern std::string redisReplyContentConv2String(OkRedisReplyType &rp, redisReply *it);

    using OkRedisNilType = void;
    using OkRedisIntType = long long int;
    using OkRedisBoolType = bool;
    using OkRedisStringType = std::string;
    using OkRedisStatusType = std::string;
    using OkRedisVecType = std::vector<std::string>;
    using OkRedisMapType = std::map<std::string, std::string>;

    class OkRedisBasicReplyContent {

    public:
        OkRedisBasicReplyContent(OkRedisReplyType &rt)
                : isHasValue(rt.hasValue()) {
        }

        OkRedisBasicReplyContent()
                : isHasValue(false) {
        }

        bool hasValue() const {
            return isHasValue;
        }

    private:
        bool isHasValue;
    };

    template<typename T>
    class OkRedisReplyContent {

    };

    template<>
    class OkRedisReplyContent<OkRedisBoolType> : public OkRedisBasicReplyContent {
    public:
        OkRedisReplyContent(OkRedisReplyType &rt, const redisReply *) :
                OkRedisBasicReplyContent(rt),
                val(hasValue() ? rt.isReplyOk() : false) {
        }

        const OkRedisBoolType getValue() const {
            return val;
        }

    private:
        OkRedisBoolType val{false};
    };

    template<>
    class OkRedisReplyContent<OkRedisIntType> : public OkRedisBasicReplyContent {

    public:

        OkRedisReplyContent(OkRedisReplyType &rt,
                            const redisReply *r) :
                OkRedisBasicReplyContent(rt),
                val(hasValue() ? r->integer : -1) {
        }

        const OkRedisIntType getValue() const {
            return val;
        }

    private:
        OkRedisIntType val{-1};
    };

    template<>
    class OkRedisReplyContent<OkRedisStringType> : public OkRedisBasicReplyContent {

    public:

        OkRedisReplyContent(OkRedisReplyType &rt, const redisReply *r) :
                OkRedisBasicReplyContent(rt) {
            if (hasValue()) {
                val = std::string(r->str, r->len);
            }
        }

        const OkRedisStringType &getValue() const {
            return val;
        }

    private:
        OkRedisStringType val{""};
    };

    template<>
    class OkRedisReplyContent<OkRedisVecType> : public OkRedisBasicReplyContent {

    public:

        OkRedisReplyContent(OkRedisReplyType &rp, const redisReply *r) :
                OkRedisBasicReplyContent(rp),
                val() {
            if (!hasValue()) {
                return;
            }
            for (size_t i = 0; i < r->elements; ++i) {
                redisReply *tmp = r->element[i];
                val.push_back(redisReplyContentConv2String(rp, tmp));
            }
        }

        const OkRedisVecType &getValue() const {
            return val;
        }

    private:
        OkRedisVecType val{};
    };

    template<>
    class OkRedisReplyContent<OkRedisMapType> : public OkRedisBasicReplyContent {
    public:

        OkRedisReplyContent(OkRedisReplyType &rp, const redisReply *r) :
                OkRedisBasicReplyContent(rp),
                val() {
            if (!hasValue()) {
                return;
            }
            for (size_t i = 0; i < r->elements; i += 2) {
                redisReply *first = r->element[i];
                redisReply *second = r->element[i + 1];
                val.insert(
                        std::make_pair(redisReplyContentConv2String(rp, first),
                                       redisReplyContentConv2String(rp, second))
                );
            }
        }

        const OkRedisMapType &getValue() const {
            return val;
        }

    private:
        OkRedisMapType val{};
    };

}


#endif //OKREDIS_OKREDISREPLY_H
